enum TEMPLATE_MARKS
{
    START = -1,
    FINISH = -2
}


var graph = argument0;
var MAX_CORRIDOR_LENGTH = argument1;
var seed = argument2;

var height = array_height_2d(graph);
var width = array_length_2d(graph, 0);

var stack = ds_stack_create();

var COMPLEXITY = 5;

var MIN_CORRIDOR_LENGTH = floor(MAX_CORRIDOR_LENGTH / COMPLEXITY) + 1

var MIN_SPACING = MIN_CORRIDOR_LENGTH + 1;

random_set_seed(seed);

var start = 0;
start[0] = irandom(height - 2) + 1;
start[1] = irandom(width  - 2) + 1;

ds_stack_push(stack, start);

//use negative numbers to mark start and finish
graph[@start[0], start[1]] = 1;

while (ds_stack_size(stack) != 0)
{
    var current = 0;
    current = ds_stack_pop(stack);
    
    //east neighbor
    var neighbor0 = 0;
    neighbor0[0] = current[0];
    neighbor0[1] = current[1] + 1;
    neighbor0[2] = false; //viableness
    
    //north neighbor
    var neighbor1 = 0;
    neighbor1[0] = current[0] - 1;
    neighbor1[1] = current[1];
    neighbor1[2] = false;
    
    //west neighbor
    var neighbor2 = 0;
    neighbor2[0] = current[0];
    neighbor2[1] = current[1] - 1;
    neighbor2[2] = false;
    
    //south neighbor
    var neighbor3 = 0;
    neighbor3[0] = current[0] + 1;
    neighbor3[1] = current[1];
    neighbor3[2] = false;
    
    //check and set diretion (dir)
    //finds the last piece of path placed
    var inverseDirection = -1;
    var dir = -1;
    if (neighbor0[1] < width && graph[neighbor0[0], neighbor0[1]] != 0)
    {
        inverseDirection = 0;
        dir = 2;
    } 
    else if (neighbor1[0] > -1 && graph[neighbor1[0], neighbor1[1]] != 0)
    {
        inverseDirection = 1;
        dir = 3;
    }
    else if (neighbor2[1] > -1 && graph[neighbor2[0], neighbor2[1]] != 0)
    {
        inverseDirection = 2;
        dir = 0;
    }
    else if (neighbor3[0] < height && graph[neighbor3[0], neighbor3[1]] != 0)
    {
        inverseDirection = 3;
        dir = 1;
    }
    
    //set minimum distance priority
    var prioritizeLength = false;
    if ( dir != -1 && getCorridorLength(graph, current, inverseDirection) < MIN_CORRIDOR_LENGTH )
    {
        prioritizeLength = true;
    }
        
    //check east bounds
    neighbor0[2] = graph[neighbor0[0], neighbor0[1]] == 0 && neighbor0[1] + MIN_SPACING < width && 
        neighbor0[0] - 2 > -1 && neighbor0[0] + 2 < height &&         
        !graph[neighbor0[0] - 1, neighbor0[1] + 1] &&
        !graph[neighbor0[0] - 2, neighbor0[1] + 1] &&
        !graph[neighbor0[0] - 1, neighbor0[1]] &&
        !graph[neighbor0[0] - 2, neighbor0[1]] &&
        !graph[neighbor0[0] + 1, neighbor0[1]] &&
        !graph[neighbor0[0] + 2, neighbor0[1]] &&
        !graph[neighbor0[0] + 1, neighbor0[1] + 1] &&
        !graph[neighbor0[0] + 2, neighbor0[1] + 1] &&
        getCorridorLength(graph, neighbor0, 2) < MAX_CORRIDOR_LENGTH &&
        isSpaceAvailable(graph, neighbor0, 0, MIN_SPACING);
    
    neighbor1[2] = graph[neighbor1[0], neighbor1[1]] == 0 && neighbor1[0] - MIN_SPACING > -1 &&
        neighbor1[1] - 2 > -1 && neighbor1[1] + 2 < width &&
        !graph[neighbor1[0], neighbor1[1] + 1] &&
        !graph[neighbor1[0], neighbor1[1] + 2] &&
        !graph[neighbor1[0] - 1, neighbor1[1] + 1] &&
        !graph[neighbor1[0] - 1, neighbor1[1] + 2] &&
        !graph[neighbor1[0] - 1, neighbor1[1] - 1] &&
        !graph[neighbor1[0] - 1, neighbor1[1] - 2] &&
        !graph[neighbor1[0], neighbor1[1] - 1] &&
        !graph[neighbor1[0], neighbor1[1] - 2] &&
        getCorridorLength(graph, neighbor1, 3) < MAX_CORRIDOR_LENGTH &&
        isSpaceAvailable(graph, neighbor1, 1, MIN_SPACING);
    
    neighbor2[2] = graph[neighbor2[0], neighbor2[1]] == 0 && neighbor2[1] - MIN_SPACING > -1 &&
        neighbor2[0] - 2 > -1 && neighbor2[0] + 2 < width &&
        !graph[neighbor2[0] - 1, neighbor2[1]] &&
        !graph[neighbor2[0] - 2, neighbor2[1]] &&
        !graph[neighbor2[0] - 1, neighbor2[1] - 1] &&
        !graph[neighbor2[0] - 2, neighbor2[1] - 1] &&
        !graph[neighbor2[0] + 1, neighbor2[1] - 1] &&
        !graph[neighbor2[0] + 2, neighbor2[1] - 1 ] &&
        !graph[neighbor2[0] + 1, neighbor2[1]] &&
        !graph[neighbor2[0] + 2, neighbor2[1]] &&
        getCorridorLength(graph, neighbor2, 0) < MAX_CORRIDOR_LENGTH &&
        isSpaceAvailable(graph, neighbor2, 2, MIN_SPACING);
    
    neighbor3[2] = graph[neighbor3[0], neighbor3[1]] == 0 && neighbor3[0] + MIN_SPACING < height &&
        neighbor3[1] - 2 > -1 && neighbor3[1] + 2 < width &&
        !graph[neighbor3[0], neighbor3[1] + 1] &&
        !graph[neighbor3[0], neighbor3[1] + 2] &&
        !graph[neighbor3[0], neighbor3[1] - 1] &&
        !graph[neighbor3[0], neighbor3[1] - 2] &&
        !graph[neighbor3[0] + 1, neighbor3[1] - 1] && 
        !graph[neighbor3[0] + 1, neighbor3[1] - 2] &&
        !graph[neighbor3[0] + 1, neighbor3[1] + 1] &&
        !graph[neighbor3[0] + 1, neighbor3[1] + 2] &&
        getCorridorLength(graph, neighbor3, 1) < MAX_CORRIDOR_LENGTH &&
        isSpaceAvailable(graph, neighbor3, 3, MIN_SPACING)
    
        
    //necessary for shuffling 
    var neighbors = 0;
    neighbors[0] = neighbor0;
    neighbors[1] = neighbor1;
    neighbors[2] = neighbor2;
    neighbors[3] = neighbor3;
    
    //if prioritize length is on, make sure to add priority meighbor last
    var priorityNeighbor = 0;
    if (prioritizeLength)
    {
        priorityNeighbor = neighbors[dir];
        if(priorityNeighbor[2] == 1)
        {
            neighbors[dir] = 0;
        }
    }
    
    //shuffle neighbors
    for (var i = 0; i < 3; i++)
    {
        var temp = neighbors[i];
        var rand = irandom(3 - i);
        neighbors[i] = neighbors[i + rand]
        neighbors[i + rand] = temp;
    }
    
    //mark viable neighbors
    for(var iii = 0; iii < 4; iii++)
    {
        var currNeighbor = neighbors[iii];
        if (is_array(currNeighbor) && currNeighbor[2] == 1)
        {
            graph[@ currNeighbor[0], currNeighbor[1]] = graph[@ current[0], current[1]] + 1;
            ds_stack_push(stack, currNeighbor);
        }
    }
    
    //add priority neighbor
    if(prioritizeLength && priorityNeighbor[2] == 1)
    {
        graph[@ priorityNeighbor[0], priorityNeighbor[1]] = graph[@ current[0], current[1]] + 1;
        ds_stack_push(stack, priorityNeighbor);
    }
}
ds_stack_destroy(stack);